package sharemem

import (
	"encoding/json"
	"time"

	"server_cluster/common/config"
	"server_cluster/common/gdconf"
	"server_cluster/common/logger"
	"server_cluster/common/protoc/pb"
	"server_cluster/common/tools"
)

const (
	RefreshMonsterHourOffset = 0
)
const (
	Uint_Type_None    = 0
	Unit_Type_Npc     = 1
	Unit_Type_Gadget  = 2
	Unit_Type_Monster = 3
)

type BaseUnit struct {
	Id       int32
	Appear   bool
	UnitType int32
}
type Monster struct {
	BaseUnit
	LastRefreshTime int64
	FirstKillDone   bool
}

func (m *Monster) ToProto() *pb.SceneEntityInfo_Monster {
	return &pb.SceneEntityInfo_Monster{
		Monster: &pb.SceneMonsterInfo{
			MonsterId: m.Id,
			IsDestroy: !m.Appear,
		},
	}
}

func (m *Monster) IsUse() bool {
	return m.Id != 0
}

func (m *Monster) MemInit() {
	m.Id = 0
	m.LastRefreshTime = 0
	m.Appear = false
	m.FirstKillDone = false
	m.UnitType = Unit_Type_Monster
}

func (m *Monster) SelfType() int32 {
	return m.UnitType
}
func (m *Monster) ToMonster() *Monster {
	return m
}

type Gadget struct {
	BaseUnit
	GadgetType  int8
	GadgetState int8
}

func (g *Gadget) ToProto() *pb.SceneEntityInfo_Gadget {
	return &pb.SceneEntityInfo_Gadget{
		Gadget: &pb.SceneGadgetInfo{
			GadgetId:    g.Id,
			IsDestroy:   !g.Appear,
			GadgetType:  pb.GadgetType(g.GadgetType),
			GadgetState: pb.GadgetState(g.GadgetState),
		},
	}
}

func (g *Gadget) IsUse() bool {
	return g.Id != 0
}

func (g *Gadget) MemInit() {
	g.Id = 0
	g.GadgetType = 0
	g.GadgetState = 0
	g.Appear = false
	g.UnitType = Unit_Type_Gadget
}

func (g *Gadget) SelfType() int32 {
	return g.UnitType
}

func (g *Gadget) ToGadget() *Gadget {
	return g
}

type Npc struct {
	BaseUnit
}

func (n *Npc) ToProto() *pb.SceneEntityInfo_Npc {
	return &pb.SceneEntityInfo_Npc{
		Npc: &pb.SceneNpcInfo{
			NpcId:     n.Id,
			IsDestroy: !n.Appear,
		},
	}
}

func (n *Npc) IsUse() bool {
	return n.Id != 0
}

func (n *Npc) MemInit() {
	n.Id = 0
	n.Appear = false
	n.UnitType = Unit_Type_Npc
}

func (n *Npc) SelfType() int32 {
	return n.UnitType
}

func (n *Npc) ToNpc() *Npc {
	return n
}

type FishInfo struct {
	FishId     int32 // 鱼id
	FishRodId  int32 // 鱼竿id
	FishBaitId int32 // 鱼饵id
	FishLen    int32 // 鱼长度
}

func (f *FishInfo) MemInit() {
	f.FishId = 0
	f.FishRodId = 0
	f.FishBaitId = 0
	f.FishLen = 0
}

type UserScene struct {
	SceneList     [SCENE_MAX]Scene // 场景列表
	CurrCollCount int32            // 当前剩余冲撞次数（用于跳过战斗）
}

func (s UserScene) MarshalJSON() ([]byte, error) {
	dbUserScene := struct {
		SceneList     []Scene // 场景列表
		CurrCollCount int32   // 当前剩余冲撞次数（用于跳过战斗）
	}{}
	for _, scene := range s.SceneList {
		if !scene.IsUse() {
			continue
		}
		dbUserScene.SceneList = append(dbUserScene.SceneList, scene)
	}
	dbUserScene.CurrCollCount = s.CurrCollCount
	data, err := json.Marshal(dbUserScene)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func (s *UserScene) UnmarshalJSON(data []byte) error {
	dbUserScene := struct {
		SceneList     []*Scene // 场景列表
		CurrCollCount int32    // 当前剩余冲撞次数（用于跳过战斗）
	}{}
	err := json.Unmarshal(data, &dbUserScene)
	if err != nil {
		return err
	}
	for index, scene := range dbUserScene.SceneList {
		s.SceneList[index] = *scene
	}
	s.CurrCollCount = dbUserScene.CurrCollCount
	return nil
}

func (s *UserScene) MemInit() {
	for i := 0; i < len(s.SceneList); i++ {
		s.SceneList[i].MemInit()
	}

}

type Scene struct {
	SceneId     int32
	SceneTag    int8
	GadgetList  [100]Gadget
	MonsterList [100]Monster
	NpcList     [100]Npc
}

func (s Scene) MarshalJSON() ([]byte, error) {
	dbScene := struct {
		SceneId     int32
		SceneTag    int8
		GadgetList  []Gadget
		MonsterList []Monster
		NpcList     []Npc
	}{}
	dbScene.SceneId = s.SceneId
	dbScene.SceneTag = s.SceneTag
	for _, subUnit := range s.GadgetList {
		if !subUnit.IsUse() {
			continue
		}
		dbScene.GadgetList = append(dbScene.GadgetList, subUnit)
	}
	for _, subUnit := range s.MonsterList {
		if !subUnit.IsUse() {
			continue
		}
		dbScene.MonsterList = append(dbScene.MonsterList, subUnit)
	}
	for _, subUnit := range s.NpcList {
		if !subUnit.IsUse() {
			continue
		}
		dbScene.NpcList = append(dbScene.NpcList, subUnit)
	}
	data, err := json.Marshal(dbScene)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func (s *Scene) UnmarshalJSON(data []byte) error {
	dbScene := struct {
		SceneId     int32
		SceneTag    int8
		GadgetList  []Gadget
		MonsterList []Monster
		NpcList     []Npc
	}{}
	err := json.Unmarshal(data, &dbScene)
	if err != nil {
		return err
	}
	s.SceneId = dbScene.SceneId
	s.SceneTag = dbScene.SceneTag
	for index, subUnit := range dbScene.GadgetList {
		s.GadgetList[index] = subUnit
	}
	for index, subUnit := range dbScene.MonsterList {
		s.MonsterList[index] = subUnit
	}
	for index, subUnit := range dbScene.NpcList {
		s.NpcList[index] = subUnit
	}
	return nil
}

func (s *Scene) IsUse() bool {
	return s.SceneId != 0
}

func (s *Scene) MemInit() {
	s.SceneId = 0
	s.SceneTag = 0
	for i := 0; i < len(s.GadgetList); i++ {
		s.GadgetList[i].MemInit()
	}
	for i := 0; i < len(s.MonsterList); i++ {
		s.MonsterList[i].MemInit()
	}
	for i := 0; i < len(s.NpcList); i++ {
		s.NpcList[i].MemInit()
	}
}

func (s *Scene) SetSceneTag(sceneTag int8) {
	s.SceneTag = sceneTag
}

func (u *User) AddScene(sceneId int32) bool {
	var pScene = u.GetSceneById(sceneId)
	if pScene != nil {
		logger.Error("add scene fail,scene is Unlock,sceneId :%v ,uid: %v", sceneId, u.GetUid())
		return false
	}
	sceneConfig := gdconf.GetSceneConfig(sceneId)
	if sceneConfig == nil {
		return false
	}
	for i := 0; i < len(u.UserScene.SceneList); i++ {
		scene := &(u.UserScene.SceneList[i])
		if scene.IsUse() {
			continue
		}
		scene.SceneId = sceneId
		scene.SceneTag = int8(sceneConfig.SceneTag)

		var length = len(scene.GadgetList)
		for j := 0; j < length; j++ {
			scene.GadgetList[j].MemInit()
		}
		length = len(scene.MonsterList)
		for j := 0; j < length; j++ {
			scene.MonsterList[j].MemInit()
		}
		length = len(scene.NpcList)
		for j := 0; j < length; j++ {
			scene.NpcList[j].MemInit()
		}

		u.Add2SceneMap(scene.SceneId, scene)
		scene.UpdateScene(u)

		return true
	}
	logger.Error("no empty scene found, uid: %v", u.GetUid())
	return false
}

func (s *Scene) AddMonster(monsterId int32, user *User) bool {
	for i := 0; i < len(s.MonsterList); i++ {
		if s.MonsterList[i].IsUse() {
			continue
		}
		monster := &(s.MonsterList[i])
		if monster.IsUse() {
			continue
		}
		monster.Id = monsterId
		monster.UnitType = Unit_Type_Monster
		monster.LastRefreshTime = 0
		monster.Appear = true
		monster.FirstKillDone = false
		user.AddMonster2Map(s.SceneId, monster)
		return true
	}
	logger.Error("no empty monster found")
	return false
}

func (s *Scene) AddGadget(gadgetId int32, gadgetType int8, defaultState int8, user *User) bool {
	for i := 0; i < len(s.GadgetList); i++ {
		gadget := &(s.GadgetList[i])
		if gadget.IsUse() {
			continue
		}
		gadget.Id = gadgetId
		gadget.GadgetType = gadgetType
		gadget.GadgetState = defaultState
		gadget.Appear = true
		user.AddGadget2Map(s.SceneId, gadget)
		return true
	}
	logger.Error("no empty gadget found")
	return false
}

func (s *Scene) AddNpc(npcId int32, user *User) bool {
	npcConfig := gdconf.GetNpcConfig(npcId)
	if npcConfig == nil {
		return false
	}
	for i := 0; i < len(s.NpcList); i++ {
		npc := &(s.NpcList[i])
		if npc.IsUse() {
			continue
		}
		npc.Id = npcId
		npc.Appear = true
		for _, appearCond := range npcConfig.AppearCondList {
			if appearCond[0] == gdconf.AppearCondAcceptQuestAppear || appearCond[0] == gdconf.AppearCondFinishQuestAppear {
				npc.Appear = false
			}
		}
		user.AddNpc2Map(s.SceneId, npc)
		return true
	}
	logger.Error("no empty npc found")
	return false
}

func (s *Scene) UpdateScene(user *User) bool {
	sceneConfig := gdconf.GetSceneConfig(s.SceneId)
	if sceneConfig == nil {
		return false
	}
	sceneData := gdconf.GetSceneData(s.SceneId)
	if sceneData == nil {
		return false
	}
	for _, monster := range sceneData.MonsterList {
		battleGroupConfig := gdconf.GetBattleGroupConfig(monster.MonsterId)
		if battleGroupConfig == nil {
			continue
		}
		pMonster := user.GetSceneMonsterById(s.SceneId, monster.MonsterId)
		if pMonster == nil {
			if battleGroupConfig.UnlockCond != nil {
				continue
			}
			ok := s.AddMonster(monster.MonsterId, user)
			if !ok {
				logger.Error("add monster fail, monsterId: %v", monster.MonsterId)
			}
		}
	}
	for _, gadget := range sceneData.GadgetList {
		pGadget := user.GetSceneGadgetById(s.SceneId, gadget.GadgetId)
		if pGadget == nil {
			ok := s.AddGadget(gadget.GadgetId, int8(gadget.GadgetType), int8(gadget.DefaultState), user)
			if !ok {
				logger.Error("add gadget fail, gadgetId: %v", gadget.GadgetId)
			}
		}
	}
	for _, npc := range sceneData.NpcList {
		pNpc := user.GetSceneNpcById(s.SceneId, npc.NpcId)
		if pNpc == nil {
			ok := s.AddNpc(npc.NpcId, user)
			if !ok {
				logger.Error("add npc fail, npcId: %v", npc.NpcId)
			}
		}
	}
	return true
}

func (s *Scene) RefreshMonster(user *User) {
	for _, monster := range user.GetSceneMonsterMap(s.SceneId) {
		battleGroupConfig := gdconf.GetBattleGroupConfig(monster.Id)
		if battleGroupConfig == nil {
			continue
		}
		switch battleGroupConfig.RefreshType {
		case gdconf.BattleGroupRefreshTypeNever:
			continue
		case gdconf.BattleGroupRefreshTypeEnterScene:
		case gdconf.BattleGroupRefreshTypeDaily:
			nowOffset := tools.ParseTimeToObj(MyShm.TimeNow).Add(-(time.Hour * RefreshMonsterHourOffset))
			lastOffset := time.Unix(monster.LastRefreshTime, 0).Add(-(time.Hour * RefreshMonsterHourOffset))
			if nowOffset.Year() == lastOffset.Year() && nowOffset.YearDay() <= lastOffset.YearDay() {
				continue
			}
			monster.LastRefreshTime = MyShm.TimeNow
		default:
			continue
		}
		logger.Debug("[RefreshMonster] monster id: %v, SceneId: %v", monster.Id, s.SceneId)
		monster.Appear = true
	}
}

func (u *User) CheckBattleGroupTypeSceneMonster(battleGroupId int32) int32 {
	scene := u.GetSceneById(u.SceneId)
	if scene == nil {
		logger.Error("scene not exist, sceneId: %v, uid: %v", u.SceneId, u.GetUid())
		return config.OBJECT_NOT_FOUND
	}
	monster := u.GetSceneMonsterById(u.SceneId, battleGroupId)
	if monster == nil {
		logger.Error("monster not exist, sceneId: %v, battleGroupId: %v, uid: %v", u.SceneId, battleGroupId, u.GetUid())
		return config.OBJECT_NOT_FOUND
	}
	if !monster.Appear {
		logger.Debug("scene fight state is hide, sceneId: %v, battleGroupId: %v, uid: %v", u.SceneId, battleGroupId, u.GetUid())
		return config.SERVER_ERROR
	}
	return config.OK
}

func (u *User) GetCurrCollCount() int32 {
	return u.UserScene.CurrCollCount
}
func (u *User) SetCurrCollCount(count int32) {
	u.UserScene.CurrCollCount = count
}

func (s *Scene) PacketMonsterMap(entityList *[]*pb.SceneEntityInfo, user *User) {
	for _, monster := range user.GetSceneMonsterMap(s.SceneId) {
		if !monster.Appear {
			continue
		}
		*entityList = append(*entityList, &pb.SceneEntityInfo{Entity: monster.ToProto()})
	}
}
func (s *Scene) PacketGadgetMap(entityList *[]*pb.SceneEntityInfo, user *User) {
	for _, gadget := range user.GetSceneGadgetMap(s.SceneId) {
		if !gadget.Appear {
			continue
		}
		*entityList = append(*entityList, &pb.SceneEntityInfo{Entity: gadget.ToProto()})
	}
}
func (s *Scene) PacketNpcMap(entityList *[]*pb.SceneEntityInfo, user *User) {
	for _, npc := range user.GetSceneNpcMap(s.SceneId) {
		if !npc.Appear {
			continue
		}
		*entityList = append(*entityList, &pb.SceneEntityInfo{Entity: npc.ToProto()})
	}
}

func (s *Scene) PacketSelf(data *pb.SC_SceneDataResp, user *User) {
	data.SceneId = s.SceneId
	data.SceneTag = int32(s.SceneTag)
	s.PacketGadgetMap(&data.EntityList, user)
	s.PacketMonsterMap(&data.EntityList, user)
	s.PacketNpcMap(&data.EntityList, user)
}
